﻿using System;
using CI.Interfaces;
using CI.Mesopotamia.Dal;
using CI.Misc;
using CI.Organism;
using NUnit.Framework;
using Rhino.Mocks;
// ReSharper disable InconsistentNaming
namespace Mesopotamia.UnitTests.Neural
{
    [TestFixture]
    public class SimpleSynapseTests
    {
        SimpleSynapse _synapse;
        private readonly IHostingEnvironment _hostingEnvironment = new CI.Mesopotamia.MSRBHost(new MockHostingEnvironmentPhysical(), new CI.Mesopotamia.ScenarioLocalXML());
        private IOrganism _organism;
        const long InitalValue = 5;
        const Int16 IntialBias = 2;
        const long IntialWait = 1;
        INeuralSystem _sns;
        ILearningRule _lr;
        MockRepository _mocks;
        uint neuralHB;

        [SetUp]
        public void Setup()
        {
            _mocks = new MockRepository();
            _lr = _mocks.DynamicMock<ILearningRule>();
            _sns = new SimpleNeuralSystem(new CI.Organism.Organism(_hostingEnvironment));
            _hostingEnvironment.Organisms = new OrganismCollection();
            _synapse = new SimpleSynapse(Guid.NewGuid(), _sns);
            _organism = new CI.Organism.Organism(_hostingEnvironment) { NeuralSystem = _sns };
            neuralHB = 1;
        }

        /// <summary>guid empty argument exception checking</summary>
        [Test, ExpectedException(typeof(ArgumentException), ExpectedMessage = "guid")]
        public void CtorEmptyGuidException()
        {
            _synapse = new SimpleSynapse(Guid.Empty, _sns, _lr);
        }

        /// <summary>neural null argument exception checking</summary>
        [Test, ExpectedException(typeof(ArgumentException), ExpectedMessage = "neuralSystem")]
        public void CtorNullNeuralException()
        {
            _synapse = new SimpleSynapse(Guid.NewGuid(), null, _lr);
        }

        /// <summary>learning rule null argument exception checking</summary>
        [Test, ExpectedException(typeof(ArgumentException), ExpectedMessage = "learningRule")]
        public void CtorNullLearningRuleException()
        {
            _synapse = new SimpleSynapse(Guid.NewGuid(), _sns, null);
        }

        /// <summary>
        /// When the Input wait has a value their should be no output
        /// </summary>
        [Test]
        public void InputWaitNotZeroOutputZero()
        {
            // set input
            _synapse.InputWait = IntialWait;
            _synapse.InputValue = InitalValue;

            // cycle the synapse
            _synapse.Evaluate(neuralHB);

            // check the output 
            Assert.AreEqual(0, _synapse.OutputValue);
        }

        /// <summary>
        /// When the input has no wait value and a input value we should get that in the output
        /// </summary>
        [Test]
        public void InputWaitZeroOutputNotZero()
        {
            // set input
            _synapse.InputWait = 0;
            _synapse.InputValue = InitalValue;
   
            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output 
            Assert.AreEqual(InitalValue, _synapse.OutputValue);
        }

        /// <summary>
        /// When there is initial input and hold check that it holds the output 
        /// </summary>
        [Test]
        public void Evaluate_OutputHoldForThree_Equal()
        {
            // set input
            _synapse.InputWait = 0;
            _synapse.InputValue = InitalValue;
            _synapse.OutputHold = 3;

            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output 
            Assert.AreEqual(InitalValue, _synapse.OutputValue);

            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output 
            Assert.AreEqual(InitalValue, _synapse.OutputValue);

            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output 
            Assert.AreEqual(InitalValue, _synapse.OutputValue);

            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output 
            Assert.AreEqual(InitalValue, _synapse.OutputValue);

            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output and now it should be dead
            Assert.AreEqual(0, _synapse.OutputValue);
        }

        /// <summary>
        /// When there is initial input and hold check that it holds the output 
        /// </summary>
        [Test]
        public void Evaluate_OutputHoldForOne_Equal()
        {
            // set input
            _synapse.InputWait = 0;
            _synapse.InputValue = InitalValue;
            _synapse.OutputHold = 1;

            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output 
            Assert.AreEqual(InitalValue, _synapse.OutputValue);

            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output 
            Assert.AreEqual(InitalValue, _synapse.OutputValue);

            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output and now it should be dead
            Assert.AreEqual(0, _synapse.OutputValue);
        }

        /// <summary>
        /// When there is initial input and no hold after its first output it should drop
        /// </summary>
        [Test]
        public void Evaluate_OutputHoldForZero_Equal()
        {
            // set input
            _synapse.InputWait = 0;
            _synapse.InputValue = InitalValue;
            _synapse.OutputHold = 0;
                       

            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output 
            Assert.AreEqual(InitalValue, _synapse.OutputValue);

            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output and now it should be dead
            Assert.AreEqual(0, _synapse.OutputValue);
        }

        /// <summary>
        /// When this a bias value adjust the output as such
        /// </summary>
        [Test]
        public void OutputBiasVetoOutputNegative()
        {
            // set the input
            _synapse.InputWait = 0;
            _synapse.InputValue = InitalValue;
            _synapse.InitInputWeight = IntialBias;

            // cycle the synapse
            _synapse.Evaluate(neuralHB++);

            // check the output and make sure it has taken the bias into account
            Assert.AreEqual((InitalValue * IntialBias), _synapse.OutputValue);

        }

        /// <summary>
        /// try the mutate synapse with null organism we should get a exception
        /// </summary>
        [Test, ExpectedException(typeof(ArgumentException), ExpectedMessage = "childOrganism")]
        public void MutateNullOrganismExecption()
        {
            _synapse.Mutate(0, null);
        }

        /// <summary>
        /// try the mutate synapse with null Neural system we should get a exception
        /// </summary>
        [Test, ExpectedException(typeof(ArgumentException), ExpectedMessage = "childOrganism has no Neural System")]
        public void MutateNullNeuralSystemExecption()
        {
            _organism.NeuralSystem = null;
            _synapse.Mutate(0, _organism);
        }

        /// <summary>
        /// try the create synapse with organism with no neural cells
        /// </summary>
        [Test, ExpectedException(typeof(ArgumentException), ExpectedMessage = "childOrganism has not enough Neural Cells")]
        public void MutateNoNeuralCellsException()
        {
            // clear the cell collection
            _organism.Cells = new CellCollection();
            _synapse.Mutate(0, _organism);
        }

        /// <summary>
        /// Test to make sure the synapse mutate works
        /// </summary>
        [Test]
        public void MutateNotNull()
        {

            // setup varibles
            _hostingEnvironment.HostingEnvironmentPhysical = MockRepository.GenerateStub<IHostingEnvironmentPhysical>();
            _hostingEnvironment.HostingEnvironmentPhysical.Stub(x => x.Registered(null)).Return(false).IgnoreArguments();
            var dal = new TestDAL();
            // create a organism
            dal.CreateiRobot(0, 0, _hostingEnvironment, true);
            // set the organism
            _organism = _hostingEnvironment.Organisms.Alive[0];

            // try creating one
            ISynapse newSynapse = _synapse.Mutate(0, _organism);

            // check and make sure we got a synapse back
            Assert.IsInstanceOf<ISynapse>(newSynapse, "Exepcted Synapse");
        }

    }
}